﻿using Nemerle;
using Nemerle.Collections;
using Nemerle.Imperative;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Nemerle2.Compiler
{
  /// <summary>
  /// Description of MacroInfo.
  /// </summary>
  public class MacroInfo : IMacroInfo
  {
    _macroInfos : array[ITypeInfo * ICustomAttribute];
    
    public this(name : string, ns : list[string], macroInfos : array[ITypeInfo * ICustomAttribute])
    {
      _macroInfos = macroInfos;
      Name        = name;
      Namespace   = ns;
    }
    
    public Name : string
    {
    	get; private set;
    }
    
    public Namespace : list[string]
    {
    	get; private set;
    }
    
    public AppliedTo : MacroTargets
    {
      [Memoize]
      get
      {
        def getMacroTargets(ti : ITypeInfo) : long
        {
          def attrs = ti.CustomAttributes.CustomAttribute["Nemerle.MacroUsageAttribute"];
          
          match (attrs)
          {
            | [head] => 
              def value = head.ArgumentValue[1];
              (value :> CustomAttributeValue.Enum).Value
              
            | []     => 0L
          }
        }
        //_macroInfos.Fold(getMacroTargets);
        mutable res = 0L;
        
        foreach ((ti, _) in _macroInfos)
          res |= getMacroTargets(ti);
          
        res :> MacroTargets
      }
    }
    
    GetArray[T, Res](attr : ICustomAttribute, index : int, convert : T -> Res) : array[Res]
    {
      def ary = (attr.PositionalArguments.Nth(index) :> CustomAttributeValue.Array).Values;
      ary.MapToArray(el => convert(el :> T))
    }
    
    public Parameters : list[MacroParameterInfo]
    {
      [Memoize]
      get
      {
        def getMacroInfo(_ti : ITypeInfo, attr : ICustomAttribute)
        {
          def paramNames         = GetArray.[CustomAttributeValue.String, _](attr, 2, _.Value);
          def paramTypes         = GetArray.[CustomAttributeValue.String, _](attr, 3, _.Value);
          def paramDefaultValues = GetArray.[CustomAttributeValue.String, _](attr, 4, _.Value);
          def isParamArrays      = GetArray.[CustomAttributeValue.Bool,   _](attr, 5, _.Value);
          def parameters         = List();
          
          foreach (name in paramNames with i)
            parameters.Add(MacroParameterInfo(name, paramTypes[i], paramDefaultValues[i], isParamArrays[i]));
            
          parameters.NToList()
        }
        
        def infos = _macroInfos.Map(getMacroInfo);
        def result = infos.FirstOrDefault(inf => inf.Exists(p => !p.Name.StartsWith("_N_wildcard", StringComparison.InvariantCulture)));
        
        if (result == null)
          if (infos.Length == 0)
            []
          else
            infos[0]
        else
          result
      }
    }
    
    public Description : string
    {
      [Memoize]
      get
      {
        def getDescription(_ti : ITypeInfo, attr : ICustomAttribute) : string
        {
          (attr.PositionalArguments.NLast() :> CustomAttributeValue.String).Value
        }
        
        def descs = _macroInfos.MapToArray(getDescription);
        def desc = descs.FirstOrDefault(desc => !desc.Contains("_N_"));
        if (desc == null)
          if (descs.Length > 0) descs[0] else ""
        else 
          desc
      }
    }
    
    public TypeInfos : list[ITypeInfo]
    {
      [Memoize] get { _macroInfos.MapToList((ti, _) => ti); }
    }
    
    public override ToString() : string{ Name }
  }
}
